//操作符
#include <stdio.h>
//1️⃣算术操作符
int main()
{
   int a=7/2;//  /除号的两端都是整数的时候，执行的是整数除法，如果两端只要有一个浮点数就执行浮点数的除法
   int b=7%2;//取模，得余数, %取模操作符的两端必须是整数
   float c=7/2.0;//浮点数除法
   printf("%d\n",a);//输出为 3   (向0取整)
   printf("%d\n",b);//输出为 1
   printf("%f\n",c);//输出为 3.500000   若改为"%.1f\n" 则输出为3.5
   return 0;
}
//如果参与取模的两个数据符号不同，在C语言中(或者其他采用向0取整的语言如：C++，Java)，余数符号，与被除数相同。
//eg: -10%3 ——>-1
//    10%-3 ——> 1
//2️⃣移位操作符 (移动的是二进制位)
//7 （正数原码、反码、补码相同）
//00000000000000000000000000000111 - 原码
//00000000000000000000000000000111 - 反码
//00000000000000000000000000000111 - 补码
//-7 （负数原码、反码、补码不同） 
//10000000000000000000000000000111 - 原码
//11111111111111111111111111111000 - 反码（原码的符号位不变，其他位按位取反）
//11111111111111111111111111111001 - 补码（反码+1）
//补码——符号位不变，其他位按位取反，+1 ——>原码
//整数在内存中存储的是补码
//①左移操作符 ：左边丢弃,右边补0
int main()
{
    int a=7;
    int b=a<<1;
    printf("%d\n",a);  //7
    printf("%d\n",b);  //14
    return 0;
}
int main()
{
    int a=-7;
    int b=a<<1;
    printf("%d\n",a);  //-7
    printf("%d\n",b);  //-14
    return 0;
}
//②右移操作符  算术右移:右边丢弃,左边补原符号位   逻辑右移:右边丢弃,左边补0
//由编译器决定是算术右移还是逻辑右移（算术右移更常见,更合理）
int main()
{
    int a=7;
    int b=a>>1;
    printf("%d\n",a);  //7
    printf("%d\n",b);  //3
    return 0;
}
int main()
{
    int a=-7;
    int b=a>>1;
    printf("%d\n",a);  //-7
    printf("%d\n",b);  //-4
    return 0;
}
//3️⃣位操作符  (只适用于整数)
// & - 按(二进制)位与     有0取0，全1取1
// | - 按(二进制)位或     有1取1，全0取0
// ^ - 按(二进制)位异或   相同为0，相异为1
//-5
//原码：10000000000000000000000000000101
//反码：11111111111111111111111111111010
//补码：11111111111111111111111111111011
int main()
{
    int a=3;  //00000000000000000000000000000011 - 3的补码
    int b=-5; //11111111111111111111111111111011 - -5的补码
    int c=a&b;//00000000000000000000000000000011 - 计算结果的补码
    printf("%d",c);//输出：3
    //%d 意味着打印一个有符号的整数
    return 0;
} 
int main()
{
    int a=3;  //00000000000000000000000000000011 - 3的补码
    int b=-5; //11111111111111111111111111111011 - -5的补码
    int c=a|b;//11111111111111111111111111111011 - 计算结果的补码
              //11111111111111111111111111111010 - 计算结果的反码
              //10000000000000000000000000000101 - 计算结果的原码
    printf("%d",c);//输出：-5
    //%d 意味着打印一个有符号的整数
    return 0;
} 
int main()
{
    int a=3;  //00000000000000000000000000000011 - 3的补码
    int b=-5; //11111111111111111111111111111011 - -5的补码
    int c=a^b;//11111111111111111111111111111000 - 计算结果的补码
              //11111111111111111111111111110111 - 计算结果的反码
              //10000000000000000000000000001000 - 计算结果的原码
    printf("%d",c);//输出：-8
    //%d 意味着打印一个有符号的整数
    return 0;
} 
//⚠️不能创建临时变量（第三个变量），实现两个数的交换  P77 P51
int main()
{
    int a=1; 
    int b=3;
    scanf("%d%d",&a,&b);
    a=a+b; //理论上会存在溢出的问题
    b=a-b; //b=(a+b)-b=a
    a=a-b; //a=(a+b)-a=b
    printf("%d %d",a,b);
    return 0;
}
//不会溢出的方法： ^支持交换律
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    a=a^b;
    b=a^b; //b=(a^b)^b=0^a=a
    a=a^b; //a=(a^b)^a=0^b=b
    return 0;
}
//⚠️练习:求一个整数存储在内存中的二进制中1的个数
//意思是：求二进制补码中1的个数
//①思路：a&1=1说明最后一位是1；a&1=0说明最后一位是0
//3    补码：00000000000000000000000000000011
//1    补码：00000000000000000000000000000001
//3&1  补码：00000000000000000000000000000001
//-3   补码：11111111111111111111111111111101
//1    补码：00000000000000000000000000000001
//-3&1 补码：00000000000000000000000000000001
//之后 a>>1(每个二进制位都移为最后一位),再进行 a&1
int main()
{
    int n,i;
    scanf("%d",&n);
    int count=0;//计数
    for(i=0;i<32;i++)
    {
        if((n&1)==1) //一定要加(),因为&的优先级低于==
        {
            count++;     //也可以写成 if(((n>>i)&1)==1)
        }                //               count++;
        n=n>>1;
    }
    printf("%d\n",count);
    return 0;
}
int qvone(int a)
{
    int count=0;
    int i=0;
    for(i=0;i<32;i++)
    {
        if((a&(1<<i))==(1<<i))
        {
            count++;
        }
    }
    return count;
}
int main()
{
    int a=0;
    scanf("%d",&a);
    int ret=qvone(a);
    printf("%d\n",ret);
    return 0;
}
//②思路：十进制中求每个数就是n%10,n/10直到n为0 可见P59
//那么二进制中就是n%2,n/2直到n为0
int count_num_of_1(unsigned int n) //无符号整型，把负数看作很大的正数
{
    int count=0;
    while(n)
    {
        if(n%2==1)
        {
            count++;
        }
        n/=2;//n=n/2;
    }
    return count;
}
int main()
{
    int n;
    scanf("%d",&n);
    int ret=count_num_of_1(n);
    printf("%d",ret);
    return 0;
}
//③n=n&n-1; 这个公式每次都会去掉二进制中的最右边的一个1，直到为0
int count_num_of_1(int n)
{
    int count=0;
    while(n) //一个数只要不是0，其二进制中就一定有1
    {
        n=n&n-1;
        count++;
    }
    return count; 
}
int main()
{
    int n;
    scanf("%d",&n);
    int ret=count_num_of_1(n);
    printf("%d",ret);
    return 0;
}
//如果要判断一个数是不是2的n次方
//2^1 10
//2^2 100
//2^3 1000 ……
//n&n-1意思是去掉二进制中的1
if(n&n-1==0)//如果条件成立，那么n就是2的n次方
{

}
//⚠️两个int(32位)整数m和n的二进制表达中，有多少个位(bit)不同？
int count_diff_bit(int m,int n)
{
    int count=0;
    int a=m^n;//相同为0，相异为1
    while(a) //一个数只要不是0，其二进制中就一定有1
    {
        a=a&a-1;
        count++;
    }
    return count; 
}
int main()
{
    int m,n;
    scanf("%d %d",&m,&n);
    int ret=count_diff_bit(m,n);
    printf("%d",ret);
    return 0;
}
//⚠️打印整数二进制的奇数位和偶数位
//获取一个整数二进制序列中所有的偶数位和奇数位，分别打印出二进制序列
//00000000000000000000000000000000 
int main()
{
    int n;
    scanf("%d",&n);
    int i=0;
    //奇数位
    for(i=30;i>=0;i-=2) //第31位向右移动30位到达最右边的第1位
    {                   //最右边的第1位移动0位
        printf("%d",(n>>i)&1);
    }
    printf("\n");
    //偶数位
    for(i=31;i>=1;i-=2) //第32位向右移动31位到达最右边的第一位
    {                   //第2位向右移动1位到达最右边的第一位
        printf("%d",(n>>i)&1);
    }
    return 0;
}
//4️⃣赋值操作符
/*
int main()
{
    int a=6;  初始化
    a=20;     赋值   =是赋值操作符，把20赋值给变量a
复合赋值符：
    a=a+3; 可简化成 a+=3;
    a=a-3;  --->   a-=3;
    a=a>>1;  --->  a>>=1;
} 
*/
//5️⃣单目操作符
//在C语言中，0表示假，非0表示真
/* ！逻辑反操作，并不是数值的关系，而是真变假假变真的逻辑问题
int main()
{
    int flag=0;（假）  若改为int flag=2;（真） 输出：
    if(!flag)  （真）                   （假）
    {
        printf("Hi\n");  输出：Hi  
    }
}   if语句只执行真命题，若假命题则自动跳过
printf("%d",!2); 输出0
printf("%d",!0); 输出1

int main()
{
    int a=0;   0改成除1外任意整数输出结果都一样
    if(a==1)
    {
        printf("xixi");  输出：
    }
    return 0;
}   a初始化值为0，而不是1，所以这个条件为假

int main()
{
    int a=3;
    if(a=0) 假  若改为(a=5) 真，输出：ss
    {
        printf("ss"); 输出：
    }
    return 0;
}

int main()
{
    int a=0;
    if(a==0)
    {
        printf("ss");  输出：ss
        return 0;
    }
}  
*/
int main()
{
    int a=9;
    printf("%p\n",&a);//int类型4字节，取出的是首地址 P34
    return 0;
}
/*    
printf("%zu\n",sizeof(3+5));  输出：4  这里并不会真的计算3+5    
printf("%zu\n",sizeof 3+5);   输出：9 先计算sizeof 3结果为4，然后+5等于9
int a=10;
printf("%zu\n",sizeof(a));   输出4  另外,a的()可以省略，变量类型可
printf("%zu\n",sizeof(int)); 输出4   int的()不可省略，数据类型不可
sizeof不可以用于计算：没有定义存储大小的数组类型，或者不知道内容的结构，void类型等。比如：sizeof(void)就是错误的表达形式。
int arr[10]={0};
printf("%zu\n",sizeof(arr)); 输出40 计算的是整个数组的大小，单位是字节
printf("%zu\n",sizeof(arr[0]));输出4
printf("%zu\n",sizeof(arr)/sizeof(arr[0]));输出10--->数组元素个数
sizeof这个操作符计算返回的结果是size_t类型的，是无符号整型的

⚠️sizeof后边的表达式是不真实参与运算的，根据表达式类型来得出大小(这也正好说明了sizeof是操作符不是函数)   见8.4.c
int main()
{
    int i=2;
    printf("%zu\n",sizeof(i++)); //输出：4 这里在编译阶段就通过类型分析出来sizeof的结果是什么了，然后把sizeof(i++)这个式子变成结果用二进制机器语言生成在.exe文件中，在运行阶段只有结果，已经看不到sizeof(i++)这个式子了。
    printf("%d\n",i);   //输出：2
    return 0;
}
*/
int main()
{
    int a=0; //0的补码：00000000000000000000000000000000
     //按位取反后的补码：11111111111111111111111111111111 
     //按位取反后的反码：11111111111111111111111111111110
     //按位取反后的原码：10000000000000000000000000000001    
    printf("%d\n",~a);  //输出：-1
    int b=3; //3的补码：00000000000000000000000000000011
     //按位取反后的补码：11111111111111111111111111111100
     //按位取反后的反码：11111111111111111111111111111011
     //按位取反后的原码：10000000000000000000000000000100
    printf("%d\n",~b); //输出：-4
    return 0;
}
int main()
{
    int a=13;
    a|=(1<<4);
    printf("%d\n",a); //29
    a&=(~(1<<4));
    printf("%d\n",a); //13
    return 0;
}
/*
int a=10;
①int b=a++;  后置++，先使用，后++
//int b=a;a=a+1;
printf("%d\n",a);输出11
printf("%d\n",b);输出10
②int b=++a;  前置++，先++，后使用
//a=a+1;int b=a;
printf("%d\n",b);输出11
printf("%d\n",a);输出11

int a=9;
printf("%d",a--);输出9
test(a--); 相当于test(9)
*/

/* 3.14字面浮点数，编译器默认理解为double类型
int a=(int)3.14；  强制类型转换：(类型)
printf("%d\n",a);  输出：3
*/
//6️⃣逻辑操作符
/*     
与：
int a=0; 假
int b=10;真
if(a&&b)    必须 (真&&真) 才会打印aa
{   
    printf("aa"); 输出：
}
printf("%d",a&&b);  输出：0
printf("%d",真&&真); 输出：1

或： (真||假)(假||真)(真||真)都能打印出
*/
int main()
{
    int i=0,a=0,b=2,c=3,d=4;
    i=a++ && ++b && d++; //a++先使用后++,a=0为假,&&左边为假右边不计算
    printf("%d %d %d %d %d",a,b,c,d,i); // 1 2 3 4 0
    //若是改为 a=1 ,则输出 2 3 3 5 1 （i==1是因为表达式的结果为真）
    return 0;
}
int main()
{
    int i=0,a=0,b=2,c=3,d=4;
    i=a++ || ++b || d++; //a++先使用后++,a=0为假,||左边为假右边计算
                         //++b先++后使用,b=3为真,||左边为真右边不计算
    printf("%d %d %d %d %d",a,b,c,d,i); // 1 3 3 4 1
    //若是改为 a=1 ,则输出 2 2 3 4 1
    return 0;
}
//7️⃣表达式求值
int a=2+6/3; //因为/优先级大于+，所以先算6/3，结果再+2
int b=2+3+4;//因为都是+，优先级相同，所以按结合性决定，从左到右计算

int a=100;//默认100是int类型
long b=100l;//long b=100;也默认100为int类型，为了让100变成long int类型，末位加l，写成100l
long long int c=100ll;//同理
float d=10.0f;//默认10.0是double类型，末位加f转换为float类型  P23
 
//①隐式类型转换
int main()
{
    char a=5;  //00000101 整型提升后：00000000000000000000000000000101
    char b=126;//01111110 整型提升后：00000000000000000000000001111110
               //       进行加法计算: 00000000000000000000000010000011  
    char c=a+b;//放入c中，只取后8位：10000011 (最高位为1，是负数)
    //字符类型的c要打印出整数，就要进行整型提升:11111111111111111111111110000011 (补码)
    //字符类型的c要打印出整数，就要进行整型提升:11111111111111111111111110000010 (反码)
    //字符类型的c要打印出整数，就要进行整型提升:10000000000000000000000001111101 (原码)
    printf("%d\n",c);//输出：-125
    printf("%c\n",c);//输出：� 因为ASCII码中-125对应的字符不可见或会显示乱码
    //ASCII码：0~127
    return 0;
}
//⚠️注意：以上是数字(整数)，而不是字符(字符'5')
//字符以ASCII码值(整数值)的二进制形式存储
int main()
{
    char c='g';//000000000000000000000000 01111010 （122）
    char a='z';//000000000000000000000000 01100111 （103）
               //000000000000000000000000 11100001 （225）
    printf("%d\n",a+c);//225  ASCII码值相加
    char b=a+c;//要把225存到有符号的字符类型b中，溢出范围，只取后8位
    //%d打印时要整型提升，最高位为1，高位补符号位
    //           111111111111111111111111 11100001 （补码）
    //           111111111111111111111111 11100000 （反码）
    //           100000000000000000000000 00011111 （原码）
    printf("%c\n",b);//�
    printf("%d",b);//-31
    return 0;
} //整型提升是过程，ASCII码值相加是方式
int main()
{
    char a=0xb6;  //182——10110110   1字节
    short b=0xb600; //46592         2字节
    int c=0xb6000000; //            4字节
    if(a==0xb6)  //整型提升，a变为负数(最高位为1)，所以不可能相等
    //也可以理解为：有符号char类型范围是-128~127，所以182溢出，无法完全取到
    printf("a");  
    if(b==0xb600) //也同理a，整型提升后变为负数或理解为值溢出范围
    printf("b");
    if(c==0xb6000000) 
    printf("c");
    return 0;
} //输出：c
//若是改为unsigned char和unsigned short，就会输出a和b
int main()
{
    char c=1;
    printf("%u ",sizeof(c)); //char类型1字节
    printf("%u ",sizeof(+c));//+c为表达式，整型提升
    printf("%u ",sizeof(-c));//-c为表达式，整型提升
    return 0;
} //输出：1 4 4 
//②算术转换
int i;//全局变量  =0
//全局变量，静态变量都是放在静态区的
//全局变量和静态变量不初始化的时候，默认会被初始化为0
//局部变量，是放在栈区的，不初始化默认为随机值
int main()
{
    i--;//-1
    //i为int类型,sizeof这个操作符计算返回的结果是size_t类型的，是无符号整型的   P22
    if(i>sizeof(i))//-1 4   int要算术转换为unsigned int,那么-1就变成了一个很大的正数
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0;
} //输出：>
//③问题表达式
int main()  //见vs2022 test2 1.c
{
	int i=1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n", ret); //10
	printf("%d\n", i); //4
	return 0;
}